using System;
using System.Drawing;
using System.Linq;
using CommonBehaviors.Actions;
using Altarboy;
using Styx;
using Styx.Helpers;
using Styx.Logic.Combat;
using Styx.Logic.Inventory;
using Styx.Logic.Pathing;
using Styx.WoWInternals;
using Styx.WoWInternals.WoWObjects;
using TreeSharp;
using Action = TreeSharp.Action;

namespace Altarboy
{
    class AltarboyComposites
    {
    

        #region Delegates
            public delegate bool SimpleBoolReturnDelegate(object context);
            public delegate WoWUnit UnitSelectionDelegate(object context);
        #endregion

        // Current Player   
        public static LocalPlayer Me { get { return StyxWoW.Me; } }



        public static Composite UseSlottedItem(SimpleBoolReturnDelegate extra, WoWItem slottedItem )
            {              
                   return new Decorator(
                    ret => extra(ret) && (
                        slottedItem != null &&
                        slottedItem.BaseAddress != 0 &&                     
                        slottedItem.Cooldown == 0                                                                                                     
                    ),                   
                    new Action(delegate{                    
                     slottedItem.Use();
                     Logger.CastLog(slottedItem.Name, Me.Name);
                    } )                    
                 );

            }

        //public static Composite WaitForCastToFinish()
        //{
        //    return new PrioritySelector(
        //        new Decorator(
        //            ret => StyxWoW.Me.IsCasting && 
        //            (StyxWoW.Me.CurrentCastTimeLeft.TotalMilliseconds > 500 && StyxWoW.Me.ChanneledCastingSpellId == 0),
        //            new PrioritySelector(
        //                new ActionAlwaysSucceed()
        //            )
        //        )
        //    );
        //}

        public static Composite CastSpell(string spellName, SimpleBoolReturnDelegate extra,  WoWUnit target)
            {
                //using current target no matter what... just testing something
                return new Decorator(
                    ret => extra(ret) && CastManager.CanCast(spellName, TargetManager.ActiveTarget ),
                    new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget); })
                      
                );
           }

        public static Composite CastSpell(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target, string Label)
        {
            //using current target no matter what... just testing something
            return new Decorator(
                ret => extra(ret) && CastManager.CanCast(spellName, TargetManager.ActiveTarget),
                new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget, Label); })

            );
        }


        public static Composite CastSpellSimple(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target)
        {
            WoWSpell spell = SpellManager.Spells[spellName];
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true) &&
                !spell.Cooldown,
                new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget); })

            );
        }

        public static Composite CastSpellSimple(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target, string Label)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true),
                new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget, Label); })

            );
        }


        public static Composite CastSpellSimple(string spellName, SimpleBoolReturnDelegate extra)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellName),
                new Action(delegate { CastManager.CastSpell(spellName, Me); })

            );
        }

        public static Composite CastSpellSimple(string spellName, SimpleBoolReturnDelegate extra, string Label)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellName),
                new Action(delegate { CastManager.CastSpell(spellName, Me, Label); })

            );
        }

        public static Composite CastSpellSimple(int spellID, SimpleBoolReturnDelegate extra, WoWUnit target)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellID, TargetManager.ActiveTarget, true),
                new Action(delegate { CastManager.CastSpell(spellID, TargetManager.ActiveTarget); })

            );
        }

        public static Composite CastSpellSimple(int spellID, SimpleBoolReturnDelegate extra, WoWUnit target, string Label)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellID, TargetManager.ActiveTarget, true),
                new Action(delegate { CastManager.CastSpell(spellID, TargetManager.ActiveTarget, Label); })

            );
        }


        public static Composite CastSpellSimple(int spellID, SimpleBoolReturnDelegate extra)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellID),
                new Action(delegate { CastManager.CastSpell(spellID, Me); })

            );
        }

        public static Composite CastSpellSimple(int spellID, SimpleBoolReturnDelegate extra, string Label)
        {
            return new Decorator(
                ret => extra(ret) && SpellManager.CanCast(spellID),
                new Action(delegate { CastManager.CastSpell(spellID, Me, Label); })

            );
        }



        //public static Composite CastDebuff(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target)
        //{
        //    //If you are reading this, this was a massive fucking pain in the ass to get to work correctly.  You're welcome.
        //    string lockname = spellName;
        //    WoWSpell spell = SpellManager.Spells[spellName];
        //    return new Sequence(
        //            new Decorator(ret => extra(ret) && EvaluateLockTime(lockname) && !spell.Cooldown && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true), new ActionAlwaysSucceed()),
        //            new Wait(2, ret => !Me.IsCasting, new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget); })),
        //            new Wait(TimeSpan.FromSeconds(5), ret => Me.CastingSpell.Name == spellName, new Action(delegate { AddLock(lockname); })),
        //            new Wait(TimeSpan.FromSeconds(5), ret => AuraManager.GetAuraTimeLeft(spellName, TargetManager.ActiveTarget, true).TotalSeconds > 10, new Action(delegate { RemoveLock(lockname); }))
        //    );
        //}


        public static Composite CastDebuffX(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target)
        {
            //If you are reading this, this was a massive fucking pain in the ass to get to work correctly.  You're welcome.
            WoWSpell spell = SpellManager.Spells[spellName];
            return new Decorator(ret => extra(ret),
                new Sequence(
                new PrioritySelector(
                    new Decorator(ret => CheckCast(spellName), new Action(delegate { Logging.Write("Blah"); return (RunStatus.Success); })),
                    new Wait(TimeSpan.FromSeconds(1),ret => !spell.Cooldown && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true) && !SpellManager.GlobalCooldown,
                       new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget);}))
                    //new Wait(TimeSpan.FromSeconds(5), ret => Me.CastingSpell.Name == spellName, new Action(delegate { AddLock(lockname); })),
                    //new Wait(TimeSpan.FromSeconds(5), ret => AuraManager.GetAuraTimeLeft(spellName, TargetManager.ActiveTarget, true).TotalSeconds > 10, new Action(delegate { RemoveLock(lockname); }))
                ),
                new Wait(TimeSpan.FromSeconds(2), ret => AuraManager.GetAuraTimeLeft(spellName, TargetManager.ActiveTarget, true).TotalSeconds > 10, new ActionAlwaysSucceed() )
                
            ));
        }


        public static bool CheckCast(string spellName)
        {
            WoWSpell spell = SpellManager.Spells[spellName];
            if (Me.CastingSpell == spell) { return true; }
            else {return false;}
        }

        //public static Composite CastDebuffX(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target)
        //{
        //    string lockname = spellName;
        //    return (
        //        new Decorator(ret => extra(ret),
        //            new Switch<int>(ret => DebuffState(spellName, lockname),
        //                //new ActionAlwaysSucceed(),
        //                new SwitchArgument<int>(1, new Action(delegate { Logging.Write("Switch 1"); CastManager.CastSpell(spellName, TargetManager.ActiveTarget); })),
        //                new SwitchArgument<int>(2, new Action(delegate { Logging.Write("Switch 2"); AddLock(lockname); })),
        //                new SwitchArgument<int>(3, new Action(delegate { Logging.Write("Switch 3"); RemoveLockX(lockname); }))
        //        //new SwitchArgument<int>(4, new Action(delegate { Logging.Write("Switch 4"); }))
        //        )
        //    ));
        //}

        //public static int DebuffState(string spellName, string lockname)
        //{
        //    WoWSpell spell = SpellManager.Spells[spellName];
        //    if ((!Me.IsCasting || (Me.IsCasting && Me.CastingSpell.Name != spellName)) && !LockExists(lockname) && !spell.Cooldown && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true)) { return 1; }
        //    if (Me.CastingSpell.Name == spellName && !LockExists(lockname)) { return 2; }
        //    if (LockExists(lockname) && AuraManager.GetAuraTimeLeft(spellName, TargetManager.ActiveTarget, true).TotalSeconds > 5) { return 3; }
        //    else { return 4; }
        //}

        //public static Composite CastDebuff(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target, string altdebuffname)
        //{
        //    //If you are reading this, this was a massive fucking pain in the ass to get to work correctly.
        //    string lockname = spellName;
        //    WoWSpell spell = SpellManager.Spells[spellName];
        //    return new Decorator(
        //        ret => extra(ret) && EvaluateLockTime(lockname) && !spell.Cooldown && SpellManager.CanCast(spellName, TargetManager.ActiveTarget, true),
        //        new Sequence(
        //            new Action(delegate { CastManager.CastSpell(spellName, TargetManager.ActiveTarget, altdebuffname); }),
        //            new Wait(TimeSpan.FromSeconds(5), ret => Me.CastingSpell.Name == spellName, new Action(delegate { AddLock(lockname); })),
        //            new WaitContinue(TimeSpan.FromSeconds(5), ret => AuraManager.GetAuraTimeLeft(altdebuffname, TargetManager.ActiveTarget, true).TotalSeconds > 5, new Action(delegate { RemoveLock(lockname); 
        //                 }))
        //    )); 
        //}

        //private static bool LockExists(string lockname)
        //{
        //    if (AltarboyHashes._spellLocks.ContainsKey(lockname)) { return true; }
        //    else { return false; }
        //}

        //private static bool EvaluateLockTime(string lockname)
        //{
        //    DateTime locktime = GetLock(lockname);
        //    DateTime now = DateTime.Now;
        //    if (locktime < now)
        //    {
        //        return true;
        //    }
        //    else
        //    {
        //        return false;
        //    }
        //}

        //private static DateTime GetLock(string lockname)
        //{
        //    if (AltarboyHashes._spellLocks.ContainsKey(lockname))
        //    {
        //        return AltarboyHashes._spellLocks[lockname];
        //    }
        //    else
        //    {
        //        return DateTime.Now.Subtract(new TimeSpan(0, 0, 20));
        //    }
        //}

        //private static void AddLock(string lockname)
        //{
        //    DateTime locktime = GetLock(lockname);
        //    AltarboyHashes._spellLocks[lockname] = DateTime.Now.AddSeconds(4);
        //    Logging.Write("Lock time is {0:HH:mm:ss}. Locking until {1:HH:mm:ss}.", locktime, DateTime.Now.AddSeconds(4));
        //}

        //private static void RemoveLock(string lockname)
        //{
        //    //will clean this up and have it remove dictionary entry completely.  was using it for logging purposes.
        //    AltarboyHashes._spellLocks[lockname] = DateTime.Now.AddSeconds(-5);
        //    Logging.Write("Resetting Lock on {0}.", lockname);
        //}

        //private static void RemoveLockX(string lockname)
        //{
        //    //will clean this up and have it remove dictionary entry completely.  was using it for logging purposes.
        //    AltarboyHashes._spellLocks.Remove(lockname);
        //    Logging.Write("Removing Lock on {0}.", lockname);
        //}

        public static Composite CastHeal(string spellName, SimpleBoolReturnDelegate extra, WoWUnit target)
        {
            //using current target no matter what... just testing something
            return new Decorator(
                ret => extra(ret) && CastManager.CanCast(spellName, target),
                new Action(delegate
            {
                CastManager.CastSpell(spellName, target);
            })

            );
        }
     
        public static Composite BuffSelf(string buffName, SimpleBoolReturnDelegate extra)
        {
            return new Decorator(
                ret => extra(ret) && !Me.Auras.Values.Any(a => a.Spell.Name == buffName) && CastManager.CanCast(buffName, Me),
                new Action(delegate
                    {
                        CastManager.CastSpell(buffName, Me); 
                    })
             );
        }


        
        public static Composite UseItemById(int itemId, SimpleBoolReturnDelegate extra)
        {
            return new PrioritySelector(
                new Decorator(
                     ret => extra(ret),
                     new Action(delegate{
                         CastManager.UseItemById(itemId);
                         Logger.Log("Using " + CastManager.GetItemById(itemId) + ". Lusting? " + AuraManager.IsLusting(Me));
                     })
                ));
        }


        //Credits Singular
        public static Composite DrinkForMana()
        {
            return new PrioritySelector( // Check if we're allowed to eat (and make sure we have some food. Don't bother going further if we have none.
                new Decorator(
                    ret => !StyxWoW.Me.IsSwimming && StyxWoW.Me.HealthPercent <= AltarboySettings.Instance.MinRestHealthPercent  && !StyxWoW.Me.HasAura("Food") && Consumable.GetBestFood(false) != null,
                    new PrioritySelector(
                        new Decorator(
                            ret => StyxWoW.Me.IsMoving,
                            new Action(ret => Navigator.PlayerMover.MoveStop())),
                        new Action(
                            ret =>
                            {
                                Styx.Logic.Common.Rest.FeedImmediate();
                                StyxWoW.SleepForLagDuration();
                            })
                        )),
                // Make sure we're a class with mana, if not, just ignore drinking all together! Other than that... same for food.
                new Decorator(
                    ret => !StyxWoW.Me.IsSwimming && StyxWoW.Me.PowerType == WoWPowerType.Mana && StyxWoW.Me.ManaPercent <= AltarboySettings.Instance.MinRestManaPercent && !StyxWoW.Me.HasAura("Drink") && Consumable.GetBestDrink(false) != null,
                    new PrioritySelector(
                        new Decorator(
                            ret => StyxWoW.Me.IsMoving,
                            new Action(ret => Navigator.PlayerMover.MoveStop())),
                        new Action(
                            ret =>
                            {
                                Styx.Logic.Common.Rest.DrinkImmediate();
                                StyxWoW.SleepForLagDuration();
                            })
                        )),
                // This is to ensure we STAY SEATED while eating/drinking. No reason for us to get up before we have to.
                new Decorator(
                    ret =>
                    (StyxWoW.Me.HasAura("Food") && StyxWoW.Me.HealthPercent < 95) || (StyxWoW.Me.HasAura("Drink") && StyxWoW.Me.PowerType == WoWPowerType.Mana && StyxWoW.Me.ManaPercent < 95),
                    new ActionAlwaysSucceed()),
                new Decorator(
                    ret => (StyxWoW.Me.PowerType == WoWPowerType.Mana && StyxWoW.Me.ManaPercent <= AltarboySettings.Instance.MinRestManaPercent) || StyxWoW.Me.HealthPercent <= AltarboySettings.Instance.MinRestHealthPercent,
                    new Action(ret => Logger.Log("We have no food/drink. Waiting to recover our health/mana back")))
                );

        }

        public static Composite CreateMoveToAndFace(float maxRange, float coneDegrees, UnitSelectionDelegate unit, bool noMovement)
        {
            return new Decorator(
                ret => unit(ret) != null,
                new PrioritySelector(
                    new Decorator(
                        ret => (!unit(ret).InLineOfSightOCD || (!noMovement && unit(ret).DistanceSqr > maxRange * maxRange)),
                        new Action(ret => Navigator.MoveTo(unit(ret).Location))),
                //Returning failure for movestop for smoother movement
                //Rest should return success !
                    new Decorator(
                        ret => Me.IsMoving && unit(ret).DistanceSqr <= maxRange * maxRange,
                        new Action(delegate
                        {
                            Navigator.PlayerMover.MoveStop();
                            return RunStatus.Failure;
                        })),
                    new Decorator(
                        ret => TargetManager.ActiveTarget != null && TargetManager.ActiveTarget.IsAlive && !Me.IsSafelyFacing(TargetManager.ActiveTarget, coneDegrees),
                        new Action(ret => TargetManager.ActiveTarget.Face()))
                    ));
        }


        #region Wanding Logic (Credit Singular)

        public static Composite CreateUseWand()
        {
            return CreateUseWand(ret => true);
        }

        public static Composite CreateUseWand(SimpleBoolReturnDelegate extra)
        {
            return new PrioritySelector(
                new Decorator(
                    ret => HasWand && !IsWanding && extra(ret),
                    new Action(ret => SpellManager.Cast("Shoot")))
                );
        }

        public static bool HasWand
        {
            get{
                return Me.Inventory.Equipped.Ranged != null &&
                       Me.Inventory.Equipped.Ranged.ItemInfo.WeaponClass == WoWItemWeaponClass.Wand;
            }
        }

        public static bool IsWanding { get { return Me.AutoRepeatingSpellId == 5019; } }

        #endregion

        #region CoolDowns
        
        
        public static Composite UseCoolDowns()
        {
            return new PrioritySelector(

                //Volcanic Potion during Bloodlust (will move this out when I can)
                //This should be using user settings now - untested.
                AltarboyComposites.UseItemById(AltarboySettings.Instance.FavouritePotionID, ret => (
                    AltarboySettings.Instance.UsePotionsOnLust &&
                    AltarboySettings.Instance.FavouritePotionID > 0 &&
                    CastManager.CanUseItem(AltarboySettings.Instance.FavouritePotionID) &&
                    AuraManager.IsLusting(Me))
                ),
                // Sharing setting for trinket and tinker usage between Archangel and Demon Soul for now.
                AltarboyComposites.UseSlottedItem(
                        ret => AltarboySettings.Instance.Trinket1UseOnCD || (AltarboySettings.Instance.Trinket1UseWithKeybuff && (AuraManager.HasMyAura("Dark Archangel", Me) || AuraManager.HasMyAura(AltarboySettings.Instance.KeyBuffWarlock, Me))),
                     StyxWoW.Me.Inventory.Equipped.Trinket1
                ),
                 AltarboyComposites.UseSlottedItem(
                        ret => AltarboySettings.Instance.Trinket2UseOnCD || (AltarboySettings.Instance.Trinket2UseWithKeybuff && (AuraManager.HasMyAura("Dark Archangel", Me) || AuraManager.HasMyAura(AltarboySettings.Instance.KeyBuffWarlock, Me))),
                     StyxWoW.Me.Inventory.Equipped.Trinket2
                ),

                 AltarboyComposites.UseSlottedItem(
                        ret => AltarboySettings.Instance.EngUseBelt,
                         StyxWoW.Me.Inventory.Equipped.Waist
                ),

                 AltarboyComposites.UseSlottedItem(
                        ret => AltarboySettings.Instance.EngUseGloves || (AltarboySettings.Instance.EngUseGlovesWithKeybuff && (AuraManager.HasMyAura("Dark Archangel", Me) || AuraManager.HasMyAura(AltarboySettings.Instance.KeyBuffWarlock, Me))),
                         StyxWoW.Me.Inventory.Equipped.Hands
                ),

                 // Blood Fury - Orc racial.  Using SpellID for Mage/Warlock Orcs.
                 AltarboyComposites.CastSpellSimple(33702, ret => AuraManager.HasMyAura(AltarboySettings.Instance.KeyBuffWarlock, Me))

            );
        }
        #endregion
    }
}
